﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * ZIP File Class
 * 		RRepresents and manages a PKZIP file.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Collections.Generic;
using Adaptive.Win32;

namespace Adaptive.Foundation.IO.Zip
{
    /// <summary>
    /// Represents and manages a PKZIP file.
    /// </summary>
    /// <remarks>
    /// 
    /// </remarks>
    public class ZipFile : AiObjectBase, IStorable
    {
        /*--------------------------------------------------------------------------------
         * Public Event Declarations
         *------------------------------------------------------------------------------*/
        #region Public Event Declarations
        /// <summary>
        /// Occurs when a file extraction starts.
        /// </summary>
        public event EventHandler ExtractStart;
        /// <summary>
        /// Occurs while extracting files to update a UI or log.
        /// </summary>
        public event ProgressEventHandler ExtractUpdate;
        /// <summary>
        /// Occurs once a file extraction process is complete.
        /// </summary>
        public event EventHandler ExtractComplete;
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// The stream to be read from or written to.
        /// </summary>
        private Stream _sourceStream;
        /// <summary>
        /// The path and name of the ZIP file.
        /// </summary>
        private string _fileName = String.Empty;
        /// <summary>
        /// The central directory instance.
        /// </summary>
        private ZipCentralDirectory _directory;
        /// <summary>
        /// The list of file entries in the ZIP file.
        /// </summary>
        private ZipEntryCollection _entryList;
        /// <summary>
        /// Flag indicating an extraction operation is in progress.
        /// </summary>
        private bool _extracting;
        /// <summary>
        /// Flag to cancel an operation.
        /// </summary>
        private bool _cancelFlag;
        /// <summary>
        /// Seperate thread for asynchronous file extraction.
        /// </summary>
        private Thread _extractThread;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="ZipFile" /> class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public ZipFile()
            : base()
        {
            _directory = new ZipCentralDirectory();
            _entryList = new ZipEntryCollection();
        }
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><b>true</b> to release both managed and unmanaged resources;
        /// <b>false</b> to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                Close();
                if (_entryList != null)
                    _entryList.Dispose();

                if (_directory != null)
                    _directory.Dispose();
            }

            _directory = null;
            _entryList = null;
            _fileName = null;
            _sourceStream = null;

            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets or sets the path and name of the ZIP file.
        /// </summary>
        /// <value>
        /// The fully-qualified path and name of the file being created or read.
        /// </value>
        /// <remarks>
        /// This value cannot be changed if a file is open.
        /// </remarks>
        public string FileName
        {
            get
            {
                return _fileName;
            }
            set
            {
                if (_sourceStream == null)
                    _fileName = value;
            }
        }
        /// <summary>
        /// Gets a value indicating whether this instance is open.
        /// </summary>
        /// <value><c>true</c> if this instance is open; otherwise, <c>false</c>.</value>
        public bool IsOpen
        {
            get
            {
                return (_sourceStream != null);
            }
        }
        /// <summary>
        /// Gets the reference to the central directory instance.
        /// </summary>
        /// <value>
        /// A <see cref="ZipCentralDirectory"/> instance containing the central directory information.
        /// </value>
        public ZipCentralDirectory Directory
        {
            get
            {
                return _directory;
            }
        }
        /// <summary>
        /// Gets the reference to the list of file entries in the ZIP file.
        /// </summary>
        /// <value>
        /// A <see cref="ZipEntryCollection"/> instance containing the entry list.
        /// </value>
        public ZipEntryCollection EntryList
        {
            get
            {
                return _entryList;
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Begins an asychronous thread for extracting the files as specified with
        /// the options provided.
        /// </summary>
        /// <remarks>
        /// This method simply launches a thread to call the <see cref="Extract"/> method.
        /// </remarks>
        /// <param name="options">
        /// A <see cref="ZipExtractOptions"/> instance containing the options specifying 
        /// how to extract the files.
        /// </param>
        public void BeginExtract(ZipExtractOptions options)
        {
            if ((_extractThread == null) && (!_extracting))
            {
                _extractThread = new Thread(new ParameterizedThreadStart(CallExtract));
                _extractThread.IsBackground = true;
                _extractThread.Start(options);
            }
        }
        /// <summary>
        /// Resets the property values for all items in the instance to their default values, and
        /// clears any internal lists or collections.
        /// </summary>
        public override void Clear()
        {
            if (_directory != null)
                _directory.Clear();

            if (_entryList != null)
                _entryList.Clear();


            base.Clear();
        }
        /// <summary>
        /// Closes the open stream.
        /// </summary>
        public void Close()
        {
            BinaryWriter writer = null;

            if (_sourceStream != null)
            {
                //Rewind the file.
                _sourceStream.Seek(0, SeekOrigin.Begin);
                writer = new BinaryWriter(_sourceStream);

                //Write everything to disk.
                Save(writer);
                writer.Flush();

                //Close and clear.
                writer.Close();
                _sourceStream.Close();
                _sourceStream = null;
                Clear();
            }
        }
        /// <summary>
        /// Creates the specified ZIP file for writing.
        /// </summary>
        /// <param name="fileName">
        /// The path and name of the file to be written to.
        /// </param>
        public void Create(string fileName)
        {
            FileStream newFile = null;

            if (_sourceStream == null)
            {
                Clear();

                if (File.Exists(fileName))
                    File.Delete(fileName);

                try
                {
                    newFile = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None);
                    _sourceStream = newFile;
                    _fileName = fileName;
                }
                catch
                {
                    newFile = null;
                }
            }
        }
        /// <summary>
        /// Waits for the asynchronous extractions to complete.
        /// </summary>
        public void EndExtract()
        {
            while ((_extracting) && (_extractThread != null) && (_extractThread.IsAlive))
                System.Threading.Thread.Sleep(0);

            _extracting = false;
            _extractThread = null;
            _cancelFlag = false;
        }
        /// <summary>
        /// Extracts the files from the archive based on the 
        /// options provided.
        /// </summary>
        /// <param name="options">
        /// A <see cref="ZipExtractOptions"/> instance containing the options specifying 
        /// how to extract the files.
        /// </param>
        public void Extract(ZipExtractOptions options)
        {
            ProgressEventArgs eventArgs = null;                 //Event arguments for UI update.
            int count = 0;                                      //Iteration counter.
            int length = 0;                                     //Iteration limit.
            string itemName = string.Empty;                     //Item name value.

            if (options == null)
                throw new ArgumentNullException("options");

            if (!_extracting)
            {
                //Raise event to UI.
                OnExtractStart(EventArgs.Empty);

                //Create objects and set flags.
                _extracting = true;
                _cancelFlag = false;
                length = options.FileNameList.Count;
                eventArgs = new ProgressEventArgs();

                //Perform file extraction unless cancelled.
                for (count = 0; count < length; count++)
                {
                    itemName = options.FileNameList[count];

                    //Update the UI.
                    if ((count % 20 == 0) || (length <= 100))
                    {
                        eventArgs.Status = itemName;
                        eventArgs.PercentComplete = (int)(((float)count / (float)length) * 100);
                        OnExtractUpdate(eventArgs);
                    }

                    if (!_cancelFlag)
                        PerformFileExtraction(EntryList[itemName], options);
                }

                //Open a folder window to the extracted data, if specified.
                if (options.OpenExplorerWindow)
                    Process.Start("explorer.exe", options.DestinationPath);

                //Complete.
                OnExtractComplete(EventArgs.Empty);
                GC.Collect();
                _extracting = false;
                _extractThread = null;
            }
            
        }
        /// <summary>
        /// Cancels a current extraction operation.
        /// </summary>
        public void CancelExtract()
        {
            if (_extracting)
                _cancelFlag = true;
        }
        /// <summary>
        /// Opens the specified ZIP file for reading.
        /// </summary>
        /// <param name="fileName">
        /// The path and name of the file to be read.
        /// </param>
        public void Open(string fileName)
        {
            FileStream newFile = null;

            if (_sourceStream == null)
            {
                Clear();

                _fileName = fileName;
                try
                {
                    newFile = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                    Open(newFile);
                }
                catch
                {
                    newFile = null;
                }
            }
        }
        /// <summary>
        /// Opens the specified source stream.
        /// </summary>
        /// <param name="sourceStream">
        /// The stream instance to be read from or written to..</param>
        public void Open(Stream sourceStream)
        {
            BinaryReader reader = null;

            if (_sourceStream == null)
            {
                _sourceStream = sourceStream;
                reader = new BinaryReader(_sourceStream);
                Load(reader);
            }
        }
        /// <summary>
        /// Opens a file for reading.
        /// </summary>
        public void Open()
        {
            Open(_fileName);
        }
        /// <summary>
        /// Loads the content for the instance from the specified stream.
        /// </summary>
        /// <param name="reader">An open <see cref="BinaryReader" /> used to read data from the
        /// underlying stream.</param>
        /// <returns>
        /// <b>true</b> if the load operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Load(BinaryReader reader)
        {
            int signatureValue = 0;
            ZipEntry newEntry = null;
            ZipCentralDirectoryEntry dirEntry = null;
            ZipArchiveExtraDataRecord extraData = null;
            int dirStart = 0;
            long oldPos = 0;

            dirStart = FindCentralDirectory();
            _sourceStream.Seek(dirStart, SeekOrigin.Begin);
            _directory.Load(reader);

            reader.BaseStream.Seek(0, SeekOrigin.Begin);

            do
            {
                //Peek at the data element signature.
                signatureValue = reader.ReadInt32();
                reader.BaseStream.Seek(-4, SeekOrigin.Current);

                //Load content based on signature value.
                switch ((ZipSignatures)signatureValue)
                {
                    case ZipSignatures.LocalFileHeader:
                        newEntry = new ZipEntry();
                        oldPos = reader.BaseStream.Position;
                        newEntry.LocalHeader.Load(reader);
                        reader.BaseStream.Seek(oldPos, SeekOrigin.Begin);

                        dirEntry = _directory.EntryList[newEntry.Name];

                        newEntry.Load(reader, false, dirEntry.CompressedSize);
                        _entryList.Add(newEntry);
                        break;

                    case ZipSignatures.ArchiveExtraData:
                        extraData = new ZipArchiveExtraDataRecord();
                        extraData.Load(reader);
                        break;

                    case ZipSignatures.CentralDirectoryHeader:
                        _directory.Load(reader);
                        break;

                    default:
                        reader.BaseStream.Seek(1, SeekOrigin.Current);
                        signatureValue = reader.ReadInt32();
                        reader.BaseStream.Seek(-4, SeekOrigin.Current);
                        break;
                }
            }
            while (reader.BaseStream.Position < reader.BaseStream.Length);

            return true;
        }
        /// <summary>
        /// Loads the compressed data for the supplied entry instance.
        /// </summary>
        /// <param name="dirEntry">
        /// The <see cref="ZipCentralDirectoryEntry"/> instance whose content is to be loaded.
        /// </param>
        /// <returns>
        /// The <see cref="ZipEntry"/> instance for the directory with the loaded compressed data.
        /// </returns>
        public ZipEntry LoadEntryData(ZipCentralDirectoryEntry dirEntry)
        {
            ZipEntry returnValue = null;

            //Get the entry.
            returnValue = _entryList[dirEntry.FileName];

            LoadEntryData(ref returnValue);
            return returnValue;
        }
        /// <summary>
        /// Loads the compressed data for the supplied entry instance.
        /// </summary>
        /// <param name="entry">
        /// The <see cref="ZipEntry"/> instance whose content is to be loaded.
        /// </param>
        public void LoadEntryData(ref ZipEntry entry)
        {
            ZipCentralDirectoryEntry dirEntry = null;
            BinaryReader reader = null;

            //Get the related directory entry.
            dirEntry = _directory.EntryList[entry.Name];
            reader = new BinaryReader(_sourceStream);

            //Go to the specified data location.
            _sourceStream.Seek(dirEntry.DataLocationInFile, SeekOrigin.Begin);
            entry.Load(reader, true, 0);

            reader = null;

        }
        /// <summary>
        /// Saves the content for the instance to the specified stream.
        /// </summary>
        /// <param name="writer">An open <see cref="BinaryWriter" /> used to write data to the
        /// underlying stream.</param>
        /// <returns>
        /// <b>true</b> if the save operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Save(BinaryWriter writer)
        {
            ZipCentralDirectoryEntry dirEntry = null;
            int position = 0;
            bool returnValue = true;

            //Write the file entries.
            writer.BaseStream.Seek(0, SeekOrigin.Begin);
            _entryList.MoveFirst();
            while (!_entryList.Eof)
            {
                //Save position data to header value while writing.
                dirEntry = _directory.EntryList[_entryList.Value.Name];
                dirEntry.DataLocationInFile = position;

                //Save the entry.
                _entryList.Value.Save(writer, true);
                _entryList.MoveNext();

                position = Convert.ToInt32(writer.BaseStream.Position);
            }

            //Save the directory.
            _directory.Save(writer);
            writer.Flush();

            return returnValue;
        }
        /// <summary>
        /// Adds the new entry to the file, entry list, and central directory.
        /// </summary>
        /// <param name="entry">
        /// The new <see cref="ZipEntry"/> value.
        /// </param>
        public void StoreNewEntry(ZipEntry entry)
        {
            ZipCentralDirectoryEntry newDirEntry = null;
            BinaryWriter writer = null;
            int position = 0;
            MsDosDate msDosDate;

            //Find the new location to write.
            position = LocatenewEntryPosition();
            _sourceStream.Seek(position, SeekOrigin.Begin);

            //Store the new data.
            writer = new BinaryWriter(_sourceStream);
            entry.Save(writer, true);
            _entryList.Add(entry);
            writer.Flush();
            writer = null;

            //Create the directory entry.
            newDirEntry = new ZipCentralDirectoryEntry();
            newDirEntry.BitFlags.Value = 0;
            newDirEntry.CompressedSize = entry.LocalHeader.CompressedSize;
            newDirEntry.CompressionMethod = CompressionMethods.Deflated;
            newDirEntry.Crc32 = entry.LocalHeader.Crc32;
            newDirEntry.DataLocationInFile = position;
            newDirEntry.DiskNumberStart = 0;
            newDirEntry.ExternalAttributes = 512;
            newDirEntry.ExtraData = null;
            newDirEntry.FileComment = "";
            newDirEntry.FileName = entry.LocalHeader.FileName;
            newDirEntry.InternalAttributes = 256;

            msDosDate = (MsDosDate)Kernel32.FileTimeToDosDate(DateTime.Now.ToFileTime());

            newDirEntry.LastModifiedDate = msDosDate.Date;
            newDirEntry.LastModifiedTime = msDosDate.Time;
            newDirEntry.UncompressedSize = entry.LocalHeader.UncompressedSize;
            newDirEntry.VersionMadeBy.Value = 20;
            newDirEntry.VersionNeeded.Value = 20;
            _directory.EntryList.Add(newDirEntry);
        }
        /// <summary>
        /// Removes the specified entries from the archive file.
        /// </summary>
        /// <param name="entryList">
        /// A <see cref="T:List"/> containing the items to be removed.
        /// </param>
        public void RemoveEntries(List<ZipEntry> entryList)
        {
            ZipFile newArchive = null;

            //Step 1: Create the ghost file.
            newArchive = CreateGhostArchiveFile();

            //Step 2: Remove specified entries.
            DeleteEntryList(entryList);

            //Step 3: Copy over any existing items from the current file to the ghost file.
            CopyEntriesToNewArchive(newArchive);

            //Step 4: Close and Delete the ghost file.
            Close();
            CopyGhostFileToPath(newArchive, _fileName);
            newArchive.Dispose();

            //Step 5: Re-Open the archive.
            Open(_fileName);

        }
        /*--------------------------------------------------------------------------------
         * Management Functions
         *------------------------------------------------------------------------------*/
        #region Management Functions
        /// <summary>
        /// Moves the archive.
        /// </summary>
        /// <param name="newPath">The new path to move the archive file to.</param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public bool MoveArchive(string newPath)
        {
            bool isOpen = false;                    //Open flag.
            bool returnValue = false;               //Return value.
            string newFileName = string.Empty;      //

            //Close the archive if open.
            isOpen = IsOpen;
            if (IsOpen)
                Close();

            try
            {
                newFileName = Path.Combine(newPath, Path.GetFileName(_fileName));
                File.Move(_fileName, newFileName);
                returnValue = true;
                _fileName = newFileName;
            }
            catch (IOException)
            {
            }

            if (isOpen)
                Open(_fileName);

            return returnValue;
        }
        /// <summary>
        /// Copies the archive from one file to another.
        /// </summary>
        /// <param name="newFileName">
        /// The name of the new file to copy the archive to.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public bool CopyArchive(string newFileName)
        {
            bool isOpen = false;                    //Open flag.
            bool returnValue = false;               //Return value.

            //Close the archive if open.
            isOpen = IsOpen;
            if (IsOpen)
                Close();

            //Perform the copy.
            try
            {
                File.Copy(_fileName, newFileName);
                returnValue = true;
                _fileName = newFileName;
            }
            catch (IOException)
            {
            }

            //Re-open the archive if it was open before.
            if (isOpen)
                Open(_fileName);

            return returnValue;
        }
        /// <summary>
        /// Re-names the archive file.
        /// </summary>
        /// <param name="newFileName">
        /// The new file name value.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public bool RenameArchive(string newFileName)
        {
            bool returnValue = false;               //Return value.
            string previousName = string.Empty;     //Previous file name.

            previousName = _fileName;

            //Copy the file to the new name and delete the old one.
            returnValue = CopyArchive(newFileName);
            if (returnValue)
            {
                try
                {
                    File.Delete(previousName);
                }
                catch (IOException)
                {
                }
            }

            return returnValue;
        }
        /// <summary>
        /// Deletes the current archive file.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public bool DeleteArchive()
        {
            bool returnValue = false;               //Return value.

            //Close the archive if open.
            if (IsOpen)
                Close();

            try
            {
                File.Delete(_fileName);
                returnValue = true;
            }
            catch (IOException)
            {
            }

            return returnValue;
        }
        #endregion

        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Event Methods
        /// <summary>
        /// Raises the <see cref="ExtractStart"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnExtractStart(EventArgs e)
        {
            if (ExtractStart != null)
                ExtractStart(this, e);
        }
        /// <summary>
        /// Raises the <see cref="E:ExtractUpdate"/> event.
        /// </summary>
        /// <param name="e">The <see cref="ProgressEventArgs"/> instance containing the event data.</param>
        protected virtual void OnExtractUpdate(ProgressEventArgs e)
        {
            if (ExtractUpdate != null)
                ExtractUpdate(this, e);
        }
        /// <summary>
        /// Raises the <see cref="E:ExtractComplete"/> event.
        /// </summary>
        /// <param name="e">
        /// A <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnExtractComplete(EventArgs e)
        {
            if (ExtractComplete != null)
                ExtractComplete(this, e);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Locates the next byte position in a ZIP file in which to write a new entry.
        /// </summary>
        /// <returns>
        /// The byte position at which to write the new entry.
        /// </returns>
        private int LocatenewEntryPosition()
        {
            int returnValue = 0;
            int count = 0;
            int length = 0;
            ZipEntry tempEntry = null;
            int signatureValue = 0;
            BinaryReader reader = null;

            //Locate the first non-file-entry signature.
            _sourceStream.Seek(0, SeekOrigin.Begin);
            length = _entryList.Count;
            tempEntry = new ZipEntry();
            reader = new BinaryReader(_sourceStream);

            for (count = 0; count < length; count++)
            {
                //Peek at the signature.
                signatureValue = reader.ReadInt32();
                _sourceStream.Seek(-4, SeekOrigin.Current);

                if (signatureValue == (int)ZipSignatures.LocalFileHeader)
                    tempEntry.Load(reader);
                else
                    returnValue = Convert.ToInt32(_sourceStream.Position);
            }
            returnValue = Convert.ToInt32(_sourceStream.Position);

            reader = null;
            tempEntry.Dispose();
            return returnValue;
        }
        private int FindCentralDirectory()
        {
            int loc = FindSignatureValue((int)ZipSignatures.EndCentralDirectory);
            ZipCentralDirectoryEndRecord tmp = null;
            BinaryReader reader = new BinaryReader(_sourceStream);
            int returnValue = 0;

            _sourceStream.Seek(loc, SeekOrigin.Begin);

            tmp = new ZipCentralDirectoryEndRecord();
            tmp.Load(reader);

            returnValue = tmp.DirectoryOffset;

            return returnValue;
        }
        private int FindSignatureValue(int signatureValue)
        {
            BinaryReader reader = null;
            int value = 0;

            _sourceStream.Seek(0, SeekOrigin.End);
            reader = new BinaryReader(_sourceStream);

            do
            {
                _sourceStream.Seek(-4, SeekOrigin.Current);
                value = reader.ReadInt32();
                if (value != signatureValue)
                    _sourceStream.Seek(-1, SeekOrigin.Current);
                else
                    _sourceStream.Seek(-4, SeekOrigin.Current);

            } while (value != signatureValue);

            return Convert.ToInt32(_sourceStream.Position);
        }
        /// <summary>
        /// Performs the file extraction from the supplied ZIP entry record.
        /// </summary>
        /// <param name="entryToExtract">
        /// A <see cref="ZipEntry"/> instance containing information about the compressed data
        /// to be extracted from the archive.
        /// </param>
        /// <param name="options">
        /// A <see cref="ZipExtractOptions"/> instance specifying how to extract the file.
        /// </param>
        private void PerformFileExtraction(ZipEntry entryToExtract, ZipExtractOptions options)
        {
            string destinationPath = string.Empty;                  //Destination path value.
            string fileName = string.Empty;                         //File name value.
            string originalPath = string.Empty;                     //Original path in archive.
            FileStream outputFile = null;                           //Output file.
            byte[] decompressedData = null;                         //Decompressed data.

            //If the folder structure is being preserved, filter the new folder name.
            if (options.UseFolderNames)
            {
                originalPath = Path.GetDirectoryName(entryToExtract.Name);
                if ((originalPath.Length > 1) && (originalPath[1] == ':'))
                    originalPath = originalPath.Substring(2, originalPath.Length - 2);

                originalPath=originalPath.Trim();
            }

            //Set the destination.
            fileName = Path.GetFileName(entryToExtract.Name);
            if (originalPath.Length > 0)
                destinationPath = Path.Combine(options.DestinationPath, originalPath);
            else
                destinationPath = options.DestinationPath;

            destinationPath = Path.Combine(destinationPath, fileName);

            //Clear the file if present and overwrite.
            if (File.Exists(destinationPath) && (options.OverwriteExistingFiles))
                File.Delete(destinationPath);

            if (!File.Exists(destinationPath))
            {
                //Decompress the file.
                LoadEntryData(ref entryToExtract);
                decompressedData = entryToExtract.Decompress();
                if ((decompressedData != null) && (decompressedData.Length > 0))
                {
                    if (!System.IO.Directory.Exists(Path.GetDirectoryName(destinationPath)))
                        System.IO.Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));

                    //Save the data.
                    outputFile = new FileStream(destinationPath, FileMode.CreateNew, FileAccess.Write);
                    outputFile.Write(decompressedData, 0, decompressedData.Length);
                    outputFile.Close();
                }
            }
        }
        /// <summary>
        /// Provides a mechanism to re-cast the parameter data to its proper type.
        /// </summary>
        /// <param name="arguments">
        /// A <see cref="ZipExtractOptions"/> as an object instance provided from parameterized
        /// thread start.
        /// </param>
        private void CallExtract(object arguments)
        {
            Extract((ZipExtractOptions)arguments);
        }
        private ZipFile CreateGhostArchiveFile()
        {
            string newFileName = string.Empty;
            ZipFile returnValue = null;

            //Create a temp file.
            newFileName = System.IO.Path.GetTempFileName();
            returnValue = new ZipFile();
            returnValue.Create(newFileName);

            return returnValue;
        }
        private void DeleteEntryList(List<ZipEntry> entryList)
        {
            foreach (ZipEntry entry in entryList)
            {
                if (_entryList[entry.Name] != null)
                    _entryList.Remove(entry.Name);

                if (_directory.EntryList[entry.Name] != null)
                    _directory.EntryList.Remove(entry.Name);
            }
        }

        private void CopyEntriesToNewArchive(ZipFile newArchive)
        {
            ZipEntry entry = null;
            Crc32 crc = new Crc32();
            _entryList.MoveFirst();
            while (!_entryList.Eof)
            {
                crc.Reset();
                entry = _entryList.Value;
                LoadEntryData(ref entry);
                entry.Compress(entry.Decompress());
                newArchive.StoreNewEntry(entry);
                _entryList.MoveNext();
            }
        }
        private void CopyGhostFileToPath(ZipFile newArchive, string fileName)
        {
            string newFile = newArchive.FileName;
            newArchive.Close();
            System.IO.File.Delete(_fileName);
            System.IO.File.Copy(newFile, _fileName);
            System.IO.File.Delete(newFile);
        }
        #endregion
    }
}